/*
 *  Copyright (C) 2010 WiiPower
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <gccore.h>
#include <ogcsys.h>
#include <sys/unistd.h>
#include <wiiuse/wpad.h>
//#include <ogc/lwp_watchdog.h>
//#include <time.h>
#include <malloc.h>
#include <stdarg.h>

#include "tools.h"
#include "storage.h"
#include "geckomenu.h"
#include "font.h"
#include "background.h"
#include "libcios.h"
#include "sha1.h"
#include "dvd_broadway.h"

extern s32 __IOS_ShutdownSubsystems();
extern void __exception_closeall();

u32 *xfb = NULL;
static GXRModeObj *rmode = NULL;
u32 screenheight;

void init_video()
{
	/* Init Video, Pad, Fonts */
	VIDEO_Init();
	init_font();

	rmode = VIDEO_GetPreferredMode(NULL);
	
	xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));

	VIDEO_Configure(rmode);

	VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK);
	VIDEO_SetNextFramebuffer(xfb);

	VIDEO_SetBlack(FALSE);

	VIDEO_Flush();

	VIDEO_WaitVSync();
	if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
    VIDEO_ClearFrameBuffer(rmode, xfb, COLOR_BLACK);
	
	screenheight = rmode->xfbHeight;
}

#define info_number 23

static u32 hashes[info_number][5] = {
{0x20e60607, 0x4e02c484, 0x2bbc5758, 0xee2b40fc, 0x35a68b0a},		// cIOSrev13a
{0x620c57c7, 0xd155b67f, 0xa451e2ba, 0xfb5534d7, 0xaa457878}, 		// cIOSrev13b
{0x3c968e54, 0x9e915458, 0x9ecc3bda, 0x16d0a0d4, 0x8cac7917},		// cIOS37 rev18
{0xe811bca8, 0xe1df1e93, 0x779c40e6, 0x2006e807, 0xd4403b97},		// cIOS38 rev18
{0x697676f0, 0x7a133b19, 0x881f512f, 0x2017b349, 0x6243c037},		// cIOS57 rev18
{0x34ec540b, 0xd1fb5a5e, 0x4ae7f069, 0xd0a39b9a, 0xb1a1445f},		// cIOS60 rev18
{0xd98a4dd9, 0xff426ddb, 0x1afebc55, 0x30f75489, 0x40b27ade},		// cIOS70 rev18
{0x0a49cd80, 0x6f8f87ff, 0xac9a10aa, 0xefec9c1d, 0x676965b9},		// cIOS37 rev19
{0x09179764, 0xeecf7f2e, 0x7631e504, 0x13b4b7aa, 0xca5fc1ab},		// cIOS38 rev19
{0x6010d5cf, 0x396415b7, 0x3c3915e9, 0x83ded6e3, 0x8f418d54},		// cIOS57 rev19
{0x589d6c4f, 0x6bcbd80a, 0xe768f258, 0xc53a322c, 0xd143f8cd},		// cIOS60 rev19
{0x8969e0bf, 0x7f9b2391, 0x31ecfd88, 0x1c6f76eb, 0xf9418fe6},		// cIOS70 rev19
{0x30aeadfe, 0x8b6ea668, 0x446578c7, 0x91f0832e, 0xb33c08ac},		// cIOS36 rev20
{0xba0461a2, 0xaa26eed0, 0x482c1a7a, 0x59a97d94, 0xa607773e},		// cIOS37 rev20
{0xb694a33e, 0xf5040583, 0x0d540460, 0x2a450f3c, 0x69a68148},		// cIOS38 rev20
{0xf6058710, 0xfe78a2d8, 0x44e6397f, 0x14a61501, 0x66c352cf},		// cIOS53 rev20
{0xfa07fb10, 0x52ffb607, 0xcf1fc572, 0xf94ce42e, 0xa2f5b523},		// cIOS55 rev20
{0xe30acf09, 0xbcc32544, 0x490aec18, 0xc276cee6, 0x5e5f6bab},		// cIOS56 rev20
{0x595ef1a3, 0x57d0cd99, 0x21b6bf6b, 0x432f6342, 0x605ae60d},		// cIOS57 rev20
{0x687a2698, 0x3efe5a08, 0xc01f6ae3, 0x3d8a1637, 0xadab6d48},		// cIOS60 rev20
{0xea6610e4, 0xa6beae66, 0x887be72d, 0x5da3415b, 0xa470523c},		// cIOS61 rev20
{0x64e1af0e, 0xf7167fd7, 0x0c696306, 0xa2035b2d, 0x6047c736},		// cIOS70 rev20
{0x0df93ca9, 0x833cf61f, 0xb3b79277, 0xf4c93cd2, 0xcd8eae17}		// cIOS80 rev20
};

static char infos[info_number][24] = {
{"cIOS rev13a"},
{"cIOS rev13b"},
{"cIOS37rev18"},
{"cIOS38rev18"},
{"cIOS57rev18"},
{"cIOS60rev18"},
{"cIOS70rev18"},
{"cIOS37rev19"},
{"cIOS38rev19"},
{"cIOS57rev19"},
{"cIOS60rev19"},
{"cIOS70rev19"},
{"cIOS36rev20"},
{"cIOS37rev20"},
{"cIOS38rev20"},
{"cIOS53rev20"},
{"cIOS55rev20"},
{"cIOS56rev20"},
{"cIOS57rev20"},
{"cIOS60rev20"},
{"cIOS61rev20"},
{"cIOS70rev20"},
{"cIOS80rev20"}
};	
	
s32 brute_tmd(tmd *p_tmd) 
{
	u16 fill;
	for(fill=0; fill<65535; fill++) 
	{
		p_tmd->fill3=fill;
		sha1 hash;
		SHA1((u8 *)p_tmd, TMD_SIZE(p_tmd), hash);;
		  
		if (hash[0]==0) 
		{
			return 0;
		}
	}
	return -1;
}	
	
void show_banner()
{
	signed_blob *TMD = NULL;
	tmd *t = NULL;
	u32 TMD_size = 0;
	u32 i;

	int ret = GetTMD((((u64)(1) << 32) | (IOS_GetVersion())), &TMD, &TMD_size);
	
	char *info;
	char default_info[32];
	sprintf(default_info, "IOS%u (Rev %u)", IOS_GetVersion(), IOS_GetRevision());
	info = (char *)default_info;

	if (ret == 0)
	{
		t = (tmd*)SIGNATURE_PAYLOAD(TMD);
		t->title_id = ((u64)(1) << 32) | 249;
		brute_tmd(t);		

		sha1 hash;
		SHA1((u8 *)TMD, TMD_size, hash);;

		free(TMD);

		for (i = 0;i < info_number;i++)
		{
			if (memcmp((void *)hash, (u32 *)&hashes[i], sizeof(sha1)) == 0)
			{
				info = (char *)&infos[i];
				sprintf(info, "%s slot %u", info, IOS_GetVersion());				
			}
		}
	}

	DrawBackground(rmode); // Show banner
	VIDEO_WaitVSync();
	if (screenheight == 480)
	{
		write_font(68, screenheight - 60,true, "Meteorloader, %s", info);
		//drawicon(xfb, 18, screenheight - 63, 32/2, 27,0); // Show Gecko icon
	} else
	{
		write_font(68, screenheight - 36,true, "Meteorloader, %s", info);
		drawicon(xfb, 18, screenheight - 39, 32/2, 27,0); // Show Gecko icon
	}
	VIDEO_WaitVSync();
}

void wait(u32 s)
{
	time_t t;
	t = time(NULL) + s; 
	while (time(NULL) < t)
	{
		// ;
		Verify_Flags(); // Standby and Reset
	}
}

void clear_status()
{
	clearscreen(xfb, 0, 210, 320, 24, 0x00800080);
}

void print_status(const char *Format, ...)
{
	char Buffer[256];
	va_list args;

	va_start(args, Format);
	vsprintf(Buffer, Format, args);

	va_end(args);

	clear_status();
	write_font(185, 210,true, Buffer);
	VIDEO_WaitVSync();
}

void *allocate_memory(u32 size)
{
	return memalign(32, (size+31)&(~31) );
}

void Verify_Flags()
{
	if (Power_Flag)
	{
		WPAD_Shutdown();
		STM_ShutdownToStandby();
	}
	if (Reset_Flag)
	{
		WPAD_Shutdown();
		STM_RebootSystem();
	}
}

void waitforbuttonpress(u32 *out, u32 *outGC, u32 *outCover)
{
	u32 pressed = 0;
	u32 pressedGC = 0;
	u32 cover_status;
	u32 old_cover_status;
	bwDVD_GetCoverStatus(&old_cover_status);

	while (true)
	{
		Verify_Flags(); // Standby and Reset

		WPAD_ScanPads();
		pressed = WPAD_ButtonsDown(0) | WPAD_ButtonsDown(1) | WPAD_ButtonsDown(2) | WPAD_ButtonsDown(3);

		PAD_ScanPads();
		pressedGC = PAD_ButtonsDown(0) | PAD_ButtonsDown(1) | PAD_ButtonsDown(2) | PAD_ButtonsDown(3);

		bwDVD_GetCoverStatus(&cover_status);
		
		if (pressed || pressedGC || (cover_status != old_cover_status)) 
		{
			if (pressedGC)
			{
				// Without waiting you can't select anything
				usleep (20000);
			}
			if (out) *out = pressed;
			if (outGC) *outGC = pressedGC;
			if (outCover) *outCover = cover_status;
			return;
		}
	}
}


void shutdown_all()
{
	storage_shutdown();
	WPAD_Shutdown();
	IRQ_Disable();
	__IOS_ShutdownSubsystems();
	__exception_closeall();
}

u32 be32(const u8 *data)
{
	return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
}
